using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;

namespace [%NAMESPACE%]
{
    public class Lookup : ContainerBase
    {
        //Format: "3;#Barak Obama"
        #region Constructors
        public Lookup(ItemBase p_parentItem, string p_fieldName, SPFieldLookup p_lookupColumn, int? p_valueId, bool p_emptyAllowed)
            : base(p_parentItem, p_fieldName, p_emptyAllowed)
        {
            if (p_parentItem == null) throw new PointBridgeEx("Lookup constructor: p_parentItem is null.");
            if (p_fieldName == null) throw new PointBridgeEx("Lookup constructor: p_fieldName is null.");

            partOfMultiSelect = false;
            parentMultiSelect = null;

            lookupWebGuid = p_lookupColumn.LookupWebId;
            if (IsGuid(p_lookupColumn.LookupList) != true) throw new PointBridgeEx("Unexpected error: lookupColumn.LookupList is not GUID, it is '" + p_lookupColumn.LookupList + "'");
            lookupListGuid = new Guid(p_lookupColumn.LookupList);
            if (p_lookupColumn.LookupField != null) lookupField = p_lookupColumn.LookupField;
            else lookupField = "Title";

            SetSelectionNoUpdate(p_valueId);
        }
        public Lookup(ItemBase p_parentItem, string p_fieldName, SPFieldLookup p_lookupColumn, int? p_valueId, string p_valueString, bool p_emptyAllowed)
            : this(p_parentItem, p_fieldName, p_lookupColumn, p_valueId, p_emptyAllowed)
        {
            if (valueString != p_valueString)
            {
                if (valueString != null && p_valueString != null) throw new PointBridgeEx(string.Format("Lookup constructor: Attemping to initialize Lookup with valudId={0} and valueString='{1}' when valueString for that valueId should be '{2}'", p_valueId, p_valueString, valueString));
                else throw new PointBridgeEx("Lookup constructor: value validation failed while one of the values (valueString or p_valueString) is null");
            }
        }

        public Lookup(ItemBase p_parentItem, string p_fieldName, Guid p_lookupWebGuid, Guid p_lookupListGuid, string p_lookupField, int? p_valueId, bool p_emptyAllowed)
            : base(p_parentItem, p_fieldName, p_emptyAllowed)
        {
            if (p_parentItem == null) throw new PointBridgeEx("Lookup constructor: p_parentItem is null.");
            if (p_fieldName == null) throw new PointBridgeEx("Lookup constructor: p_fieldName is null.");
            if (p_lookupField == null) throw new PointBridgeEx("Lookup constructor: p_lookupField is null.");

            partOfMultiSelect = false;
            parentMultiSelect = null;

            lookupWebGuid = p_lookupWebGuid;
            lookupListGuid = p_lookupListGuid;
            lookupField = p_lookupField;

            SetSelectionNoUpdate(p_valueId);
        }
        public Lookup(ItemBase p_parentItem, string p_fieldName, Guid p_lookupWebGuid, Guid p_lookupListGuid, string p_lookupField, int? p_valueId, string p_valueString, bool p_emptyAllowed)
            : this(p_parentItem, p_fieldName, p_lookupWebGuid, p_lookupListGuid, p_lookupField, p_valueId, p_emptyAllowed)
        {
            if (valueString != p_valueString)
            {
                if (valueString != null && p_valueString != null) throw new PointBridgeEx(string.Format("Lookup constructor: Attemping to initialize Lookup with valudId={0} and valueString='{1}' when valueString for that valueId should be '{2}'", p_valueId, p_valueString, valueString));
                else throw new PointBridgeEx("Lookup constructor: value validation failed while one of the values (valueString or p_valueString) is null");
            }
        }

        public Lookup(MultiLookup p_parentMultiSelect, int p_valueId)
            : base(null, null, false)
        {
            if (p_parentMultiSelect == null) throw new PointBridgeEx("Lookup constructor: p_parentMultiSelect is null.");

            partOfMultiSelect = true;
            parentMultiSelect = p_parentMultiSelect;

            lookupWebGuid = p_parentMultiSelect.lookupWebGuid;
            lookupListGuid = p_parentMultiSelect.lookupListGuid;
            lookupField = p_parentMultiSelect.lookupField;

            SetSelectionNoUpdate(p_valueId);
            if (valueString == null) throw new PointBridgeEx("Lookup constructor: valueString is null in a Lookup that is part of a Multi-Select");
        }
        public Lookup(MultiLookup p_parentMultiSelect, int p_valueId, string p_valueString)
            : this(p_parentMultiSelect, p_valueId)
        {
            if (p_valueString == null) throw new PointBridgeEx("Lookup constructor: p_valueString is null");
            if (valueString != p_valueString) throw new PointBridgeEx(string.Format("Lookup constructor: Attemping to initialize Lookup with valudId={0} and valueString='{1}' when valueString for that valueId should be '{2}'", p_valueId, p_valueString, valueString));
        }
        #endregion

        #region Methods
        public override void SetEmpty()
        {
            if (partOfMultiSelect == true) throw new PointBridgeEx("Choice.SetEmpty(): SetEmpty cannot be called for objects that are part of Multi-Select.");

            _valueId = null;
            _valueString = null;

            //either update itself or ask parent to update everyhting
            if (partOfMultiSelect != true) ForceUpdate();
            else parentMultiSelect.ForceUpdate();
        }
        public virtual void ChangeSelection(int newValueId)
        {
            if (newValueId == -1)
            {
                SetEmpty();
                return;
            }

            SetSelectionNoUpdate(newValueId);

            //either update itself or ask parent to update everyhting
            if (partOfMultiSelect != true) ForceUpdate();
            else parentMultiSelect.ForceUpdate();
        }
        public SPListItem GetReferencedItem()
        {
            if (isEmpty == true) throw new PointBridgeEx("Lookup.GetReferncedItem(): object is empty");
            return GetReferencedItem((int)valueId);
        }
        public SPListItem GetReferencedItem(int p_valueId)
        {
            return GetReferencedItem(site, lookupWebGuid, lookupListGuid, p_valueId);
        }
        public static SPListItem GetReferencedItem(SPSite site, Guid p_lookupWebGuid, Guid p_lookupListGuid, int p_id)
        {
            using (SPWeb web = site.OpenWeb(p_lookupWebGuid))
            {
                SPList list;

                try { list = web.Lists[p_lookupListGuid]; }
                catch (Exception ex) { throw new PointBridgeEx(string.Format("Unable to lookup list ({0}) from web ({1}): {2}", p_lookupListGuid, p_lookupWebGuid, ex.Message)); }

                try { return list.Items.GetItemById((int)p_id); }
                catch (Exception ex) { throw new PointBridgeEx(string.Format("Unable to lookup list item with Id={0} from list ({1}) in web ({2}): {3}", p_id, p_lookupListGuid, p_lookupWebGuid, ex.Message)); }
            }
        }
        public SPList GetReferencedList()
        {
            SPList list;
            using (SPWeb web = site.OpenWeb(lookupWebGuid))
            {
                try { list = web.Lists[lookupListGuid]; }
                catch (Exception ex) { throw new PointBridgeEx(string.Format("Unable to lookup list ({0}) from web ({1}): {2}", lookupListGuid, lookupWebGuid, ex.Message)); }
            }
            return list;
        }
        public static string GetSelectionStrValue(SPSite site, Guid p_lookupWebGuid, Guid p_lookupListGuid, string p_lookupField, int p_valueId)
        {
            SPListItem refItem = GetReferencedItem(site, p_lookupWebGuid, p_lookupListGuid, p_valueId);
            try
            {
                return refItem[p_lookupField].ToString();
            }
            catch (Exception ex)
            {
                throw new PointBridgeEx(string.Format("Field '{0}' does not exist in item (ID={1}) from list ({2}) in web ({3}): {4}", p_lookupField, p_valueId, p_lookupListGuid, p_lookupWebGuid, ex.Message));
            }
        }
        public override void ForceUpdate()
        {
            if (partOfMultiSelect == true) throw new PointBridgeEx("Lookup.ForceUpdate(): Attempt to force update parentItem when this lookup is part of a multi-select");
            parentItem.item[fieldName] = ToSPRepresentation();
        }
        public override void InitFromSPRepresentation(object obj)
        {
            if (obj == null)
            {
                if (emptyAllowed == true)
                {
                    _valueId = null;
                    _valueString = null;
                    return;
                }
                else
                {
                    throw new NullEx("Lookup.InitFromSPRepresentation(): SharePoint value is null while emptyAllowed is false. Use [Property Name]EmptyAllowed property if this is intended.");
                }
            }
            string lookupValue = (string)obj;
            if (lookupValue == "#ERR")
            {
                _valueId = null;
                _valueString = null;
                return;
            }
            string[] separators = new string[1];
            separators[0] = ";#";
            string[] splits = lookupValue.Trim().Split(separators, StringSplitOptions.None);
            if (splits.Length != 1 && splits.Length != 2) throw new PointBridgeEx("Lookup string ('" + lookupValue + "') is in invalid format");
            _valueId = int.Parse(splits[0]);
            if (splits.Length == 1) _valueString = UpdateSelectionStrValue(); //string with just id is also possible (length == 1)
            if (splits.Length == 2) _valueString = splits[1];
        }
        public override object ToSPRepresentation()
        {
            if (isEmpty == true)
            {
                if (emptyAllowed == true) return null;
                else throw new PointBridgeEx("Lookup.ToSPRepresentation(): object is not initialized while emptyAllowed is false");
            }
            return string.Format("{0};#{1}", valueId, valueString);
        }
        public override string ToString()
        {
            if (isEmpty == true) return "<Empty>";
            else return valueString;
        }
        #endregion

        #region Private Helper Methods
        private void SetSelectionNoUpdate(int? newValueId)
        {
            if (newValueId == null && partOfMultiSelect == true) throw new PointBridgeEx("Lookup.SetSelectionNoUpdate(): newValueId cannot be null for lookups that are part of Multi-Select.");
            if (newValueId != null) _valueString = GetSelectionStrValue(site, lookupWebGuid, lookupListGuid, lookupField, (int)newValueId);
            else _valueString = null;

            _valueId = newValueId;
        }
        private string UpdateSelectionStrValue()
        {
            if (isEmpty == true) throw new PointBridgeEx("Lookup.GetSelectionStrValue: Unable to update steValue becaue Lookup is empty");
            return GetSelectionStrValue(site, lookupWebGuid, lookupListGuid, lookupField, (int)valueId);
        }
        public static bool IsGuid(string expression)
        {
            if (expression != null)
            {
                Regex guidRegEx = new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");

                return guidRegEx.IsMatch(expression);
            }
            return false;
        }
        #endregion

        #region Public Properties
        public override bool isEmpty
        {
            get
            {
                if (_valueId == null) return true;
                else return false;
            }
        }
        public int valueId
        {
            get
            {
                if (isEmpty == true) throw new PointBridgeEx("class Lookup: Attempting to access 'valueId' while this class container is empty.");
                return (int)_valueId;
            }
        }
        public string valueString
        {
            get
            {
                if (isEmpty == true) throw new PointBridgeEx("class Lookup: Attempting to access 'valueString' while this class container is empty.");
                return _valueString;
            }
        }
        public SPSite site
        {
            get
            {
                return web.Site;
            }
        }
        public SPWeb web
        {
            get
            {
                if (partOfMultiSelect == true) return parentMultiSelect.parentItem.item.Web;
                else return parentItem.item.Web;
            }
        }

        public readonly bool partOfMultiSelect;
        public readonly MultiLookup parentMultiSelect;

        public readonly Guid lookupWebGuid;
        public readonly Guid lookupListGuid;
        public readonly string lookupField;
        #endregion

        #region Internal Fields
        private int? _valueId = null;
        private string _valueString = null;
        #endregion
    }
}
